/*
 * MoneyGraph.java
 *
 * Created on 8. August 2006, 13:10
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package MoneyGraph;

import com.sun.org.apache.xalan.internal.xsltc.util.IntegerArray;
import java.awt.BasicStroke;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Point2D;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Random;

/**
 *
 * @author chunke
 */
public class MoneyGraph extends Canvas{
    protected ArrayList<MoneyGraphValue> values;
    protected boolean graphDirectionColors;
    protected boolean graphFilling;
    protected int graphFillingAlpha;
    protected boolean showNullLine;
    
    /** Creates a new instance of MoneyGraph */
    public MoneyGraph() {
        values = new ArrayList<MoneyGraphValue>();
        Random r = new Random();
        for (int i = 0; i < 24; i++)
        {
            values.add(new MoneyGraphValue((r.nextDouble() - 0.5) * 10, new Date()));
        }
        graphDirectionColors = false;
        graphFilling = true;
        graphFillingAlpha = 75;
        showNullLine = true;
    }
    
    public void paint(Graphics g)
    {
        Graphics2D g2d = (Graphics2D)g;        
        
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                             RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        
        g2d.setFont(new Font("SansSerif", Font.BOLD, 10));
        FontMetrics metrics = g2d.getFontMetrics();
        double dblMaxValue = 0;
        for (int i = 0; i < values.size(); i++)
        {
            if (dblMaxValue < values.get(i).getValue())
                dblMaxValue = values.get(i).getValue();
        }
        double dblMinValue = 0;
        for (int i = 0; i < values.size(); i++)
        {
            if (dblMinValue > values.get(i).getValue())
                dblMinValue = values.get(i).getValue();
        }
        
        String strMaxValue = NumberFormat.getCurrencyInstance().format(dblMaxValue);
        int intGraphXOffset = metrics.stringWidth(strMaxValue) + metrics.getAscent();
        int intGraphYOffset = metrics.stringWidth(new SimpleDateFormat("dd.MM.yy").format(new Date())) + 3;
        Rectangle rectValues = new Rectangle(intGraphXOffset, metrics.getAscent() / 2, this.getWidth() - intGraphXOffset - 1, this.getHeight() - intGraphYOffset - metrics.getAscent() / 2);
                
        // Draw background
        Paint pTmp = g2d.getPaint();
        GradientPaint gradient = new GradientPaint(new Point2D.Float((float)rectValues.getX(), 
                (float)rectValues.getY()), new Color(230, 230, 255), new Point2D.Float((float)rectValues.getX(), 
                (float)(rectValues.getY() + rectValues.getHeight() / 2)), Color.WHITE);
        Rectangle rectControl = new Rectangle(0, 0, this.getWidth(), this.getHeight());
        g2d.setPaint(gradient);
        g2d.fill(rectControl);
        g2d.setPaint(pTmp);
        g2d.drawPolyline(new int[] { intGraphXOffset, intGraphXOffset, this.getWidth() },
                new int[] { 0, this.getHeight() - intGraphYOffset, this.getHeight() - intGraphYOffset }, 3);
        
        Stroke stroke = g2d.getStroke();
        Color color = g2d.getColor();
        
        g2d.drawString(strMaxValue, 0, metrics.getAscent());                
        g2d.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 0, new float[] { 4, 4 }, 0));
        g2d.setColor(new Color(200, 200, 200));
        g2d.drawLine(intGraphXOffset, metrics.getAscent() / 2, (int)(rectValues.getX() + rectValues.getWidth()), metrics.getAscent() / 2);
        g2d.setStroke(stroke);
        g2d.setColor(color);
        g2d.drawString(NumberFormat.getCurrencyInstance().format(dblMinValue), 0, this.getHeight() - intGraphYOffset + metrics.getAscent() / 2);
        DrawValueLegend(g2d, metrics.getAscent(), (int)(this.getHeight() - intGraphYOffset - metrics.getAscent() * 1.5), metrics.getAscent(), 
                dblMaxValue, dblMinValue, rectValues);
        // NullLine
        if (showNullLine && dblMinValue < 0)
        {
            g2d.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 0, new float[] { 2, 2 }, 0));
            g2d.setColor(Color.RED);
            double dblNullLine = rectValues.getY() + (rectValues.getHeight() * (dblMaxValue / (dblMaxValue - dblMinValue)));
            //g2d.drawString(NumberFormat.getCurrencyInstance().format((double)0), 0, (int)dblNullLine + metrics.getAscent() / 2);
            g2d.drawLine((int)rectValues.getX(), (int)dblNullLine, (int)(rectValues.getX() + rectValues.getWidth()), (int)dblNullLine);
            g2d.setStroke(stroke);
            g2d.setColor(color);
        }
        
        // DateLegend
        g2d.rotate(Math.toRadians(90));
        int dateOffset = 0;
        String strDate;
        int dateStepOffset = (int)Math.floor(rectValues.getWidth() / (values.size() - 1));
        int dateLegendStepOffset = dateStepOffset;
        if (dateStepOffset > 1)
        {
            int runs = 0;
            while (dateLegendStepOffset < metrics.getAscent() * 2 && runs < 100)
            {
                dateLegendStepOffset += dateStepOffset;
                runs++;
            }
        }
        for (int i = 0; i < values.size(); i++)
        {     
            strDate = new SimpleDateFormat("dd.MM.yy").format(values.get(i).getDate());
            g2d.drawString(strDate, this.getHeight() - metrics.stringWidth(strDate), -intGraphXOffset + metrics.getAscent() / 2 - (i * dateLegendStepOffset));
        }
        g2d.rotate(Math.toRadians(-90));               
        g2d.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 0, new float[] { 4, 4 }, 0));
        g2d.setColor(new Color(200, 200, 200));
        for (int i = 1; i < values.size(); i++)
            g2d.drawLine(intGraphXOffset + (int)(i * dateLegendStepOffset), 0, intGraphXOffset + (int)(i * dateLegendStepOffset), this.getHeight() - intGraphYOffset);
        g2d.setStroke(stroke);
        g2d.setColor(color);
        
        // Values
        int[] xCoords = new int[values.size()];
        int[] yCoords = new int[values.size()];        
        for (int i = 0; i < values.size(); i++)
        {
            xCoords[i] = (int)(intGraphXOffset + i * dateStepOffset);
            yCoords[i] = (this.getHeight() - intGraphYOffset - (int)(rectValues.getHeight() * (1 / ((dblMaxValue - dblMinValue) / (values.get(i).getValue() - dblMinValue)))));
        }        
        g2d.setColor(Color.BLUE);
        g2d.drawPolyline(xCoords, yCoords, values.size());
        g2d.setColor(color);
        if (graphFilling)
        {
            if (graphDirectionColors)
            {
                for (int i = 0; i < values.size() - 1; i++)
                {
                    int[] xCoordsFill = new int[4];
                    int[] yCoordsFill = new int[4];
                    xCoordsFill[0] = xCoords[i];
                    xCoordsFill[1] = xCoords[i + 1];
                    xCoordsFill[2] = xCoords[i + 1];
                    xCoordsFill[3] = xCoords[i];
                    yCoordsFill[0] = yCoords[i];
                    yCoordsFill[1] = yCoords[i + 1];
                    yCoordsFill[2] = this.getHeight() - intGraphYOffset;
                    yCoordsFill[3] = yCoordsFill[2];
                    if (values.get(i).getValue() < values.get(i + 1).getValue())
                        g2d.setColor(new Color(0, 255, 0, graphFillingAlpha));
                    else if (values.get(i).getValue() > values.get(i + 1).getValue())
                        g2d.setColor(new Color(255, 0, 0, graphFillingAlpha));
                    else
                        g2d.setColor(new Color(0, 0, 255, graphFillingAlpha));
                    g2d.fillPolygon(xCoordsFill, yCoordsFill, 4);
                }
            }
            else
            {
                int[] xCoordsFill = new int[values.size() + 2];
                int[] yCoordsFill = new int[values.size() + 2];
                for (int i = 0; i < values.size(); i++)
                {
                    xCoordsFill[i] = xCoords[i];
                    yCoordsFill[i] = yCoords[i];
                }
                xCoordsFill[values.size()] = xCoordsFill[values.size() - 1];
                xCoordsFill[values.size() + 1] = xCoordsFill[0];
                yCoordsFill[values.size()] = this.getHeight() - intGraphYOffset;
                yCoordsFill[values.size() + 1] = yCoordsFill[values.size()];
                g2d.setColor(new Color(0, 0, 255, graphFillingAlpha));
                g2d.fillPolygon(xCoordsFill, yCoordsFill, values.size() + 2);
            }
            g2d.setColor(color);
        }
    }
    
    private void DrawValueLegend(Graphics2D g2d, int intXOffset, int space, int height, double maxValue, double minValue, Rectangle rectValues)
    {
        if (space > height * 4)
        {
            double value = minValue + ((maxValue - minValue) / 2);
            double valueBottom = intXOffset + space / 2 + height / 2;
            double valueTop = intXOffset + space / 2 - height / 2;
            double newspace = space / 2 - height / 2;
            g2d.drawString(NumberFormat.getCurrencyInstance().format(value), 0, intXOffset + space / 2 + height / 2);
            Stroke stroke = g2d.getStroke();
            g2d.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 0, new float[] { 4, 4 }, 0));
            Color color = g2d.getColor();
            g2d.setColor(new Color(150, 150, 150));
            g2d.drawLine((int)rectValues.getX(), intXOffset + space / 2, (int)(rectValues.getX() + rectValues.getWidth()), intXOffset + space / 2);
            g2d.setStroke(stroke);
            g2d.setColor(color);
            DrawValueLegend(g2d, intXOffset, (int)newspace, height, maxValue, value, rectValues);
            DrawValueLegend(g2d, intXOffset + space / 2 + height / 2, (int)newspace, height, value, minValue, rectValues);
        }
    }

    public ArrayList<MoneyGraphValue> getValues() {
        return values;
    }

    public void setValues(ArrayList<MoneyGraphValue> values) {
        this.values = values;
    }

    public boolean isGraphDirectionColors() {
        return graphDirectionColors;
    }

    public void setGraphDirectionColors(boolean graphDirectionColors) {
        this.graphDirectionColors = graphDirectionColors;
    }

    public boolean isGraphFilling() {
        return graphFilling;
    }

    public void setGraphFilling(boolean graphFilling) {
        this.graphFilling = graphFilling;
    }

    public int getGraphFillingAlpha() {
        return graphFillingAlpha;
    }

    public void setGraphFillingAlpha(int graphFillingAlpha) {
        this.graphFillingAlpha = graphFillingAlpha;
    }

    public boolean getShowNullLine() {
        return showNullLine;
    }

    public void setShowNullLine(boolean showNullLine) {
        this.showNullLine = showNullLine;
    }   
}